package com.example.jcservice.service;

import com.dtflys.forest.http.ForestCookie;
import com.dtflys.forest.http.ForestResponse;
import com.dtflys.forest.springboot.annotation.ForestScan;
import com.example.jcservice.client.Jw;
import com.example.jcservice.stType.Resive.Jw.ExamInfo;
import com.example.jcservice.stType.Resive.Jw.JwUserInfo;
import com.example.jcservice.stType.Resive.Jw.LoginInfo;
import com.example.jcservice.stType.Resive.Jw.ScoresInfo;
import com.example.jcservice.stType.Send.ExamBody;
import com.example.jcservice.stType.Send.ScrodBody;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

@Service
@ForestScan(basePackages = "com/example/jcservice/client")
public class JwService {
    @Resource
    //spring的依赖注入写法
    private Jw jw;
    private ForestCookie loginCookie;

    ScoresInfo scoresInfo = new ScoresInfo();
    ExamInfo examInfo = new ExamInfo();

    public ArrayList<String> SoresInfo_text = new ArrayList();//用于存放第一步网页爬取的 成绩 String数据
    public ArrayList<String> ExamInfo_text = new ArrayList();//用于存放第一步网页爬取的 成绩 String数据
    public ArrayList<String> ExamAgainInfo_text = new ArrayList();//用于存放第一步网页爬取的补考考试成绩 String数据

    /**
     * 访问登录界面方法
     *第一步先获取到登录界面的 session 以及 __VIEWSTATE
     * @return*/
    public LoginInfo loginBefore() throws UnsupportedEncodingException {
        //登录前，访问登录页面拿到cookie
        AtomicReference<ForestCookie> cookieAtomic = new AtomicReference<>(null);
        ForestResponse res = jw.loginBefore( (request, cookies) -> {
            cookieAtomic.set(cookies.allCookies().get(0));
            loginCookie = cookieAtomic.get();

            System.out.println("登录界面cookie"+loginCookie.getValue());
        });

        //拿到页面响应结果 通过jsoup获取 __VIEWSTATE
        //res.getContent();就是拿到页面的响应内容
        String c = res.getContent();
//        System.out.println(c);

        Document login_before_html = Jsoup.parse(c);
        Elements VIEWSTATE = login_before_html.select("#form1 > input:nth-child(1)");
        String value = VIEWSTATE.toString();
        //b = <input type="hidden" name="__VIEWSTATE" value="dDwtMTM0OTIyMDA2Nzs7PgSVB0HGprXIT7XUpicto1jCEqua">
        //正则表达
        String __VIEWSTATE_value= URLEncoder.encode(value.substring(value.indexOf("ue=")+4, value.indexOf(">")-1), "gb2312");
        System.out.println("__VIEWSTATE_value值为："+__VIEWSTATE_value);
        System.out.println("请求登录页面cookie为："+loginCookie);

        //把登录页面获取到的cookie中的sessionId\__VIEWSTATE_value返回给前端
        LoginInfo loginInfo = new LoginInfo();

        loginInfo.setSessionId(loginCookie.getValue());
        loginInfo.set__VIEWSTATE(__VIEWSTATE_value);

        System.out.println(loginInfo.getSessionId()+"\n"+loginInfo.get__VIEWSTATE());

        return loginInfo;
    }

    /**
     * 获取验证码方法
     * */
    public InputStream getCheckCode(String session) throws Exception {
        //创建一个Forest类型的cookie，需要将前端传来的session set进去
        loginCookie.setValue(session);
        ForestResponse checkCode = jw.getCheckCode( (request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        // 以输入流的形式获取请求响应内容
        InputStream in = checkCode.getInputStream();

        System.out.println("获取验证码界面cookie"+loginCookie.getValue());
        return in;
    }

    /**
     * 模拟登录教务,需要将前端传入的session注入forest cookie中
     * */
    public JwUserInfo login(String username, String password, String checkcode,String __VIEWSTATE_value, String session){
        loginCookie.setValue(session);
        JwUserInfo userInfo = new JwUserInfo();//用于存放登录状态，以及部分信息
        String Pass = null;//定义密码错误次数为空
        String checkPassword = null;
        int out = 0;//

        //将前端传入的学号和密码，携带VIEWSTATE和cookie开始登录
        //创建一个HashMap，方便把，请求需要带的参数封装成hashmap对象，直接向接口丢map
        Map<String, String> loginJw = new HashMap<String, String>();
        loginJw.put("__VIEWSTATE",__VIEWSTATE_value);
        loginJw.put("txtUserName",username);
        loginJw.put("TextBox2",password);
        loginJw.put("txtSecretCode",checkcode);
        loginJw.put("RadioButtonList1","%D1%A7%C9%FA");//gb2312 urlencode转码，这里是 学生 的意思
        loginJw.put("Button1","");
        loginJw.put("lbLanguage","");
        loginJw.put("hidPdrs","");
        loginJw.put("hidsc","");

        ForestResponse login = jw.JwLogin(loginJw, (request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        System.out.println("登录时cookie"+loginCookie.getValue());

        String a = login.getContent();
        Document login_after_html = Jsoup.parse(a);
        //System.out.println(login_after_html);
        Elements name_text = login_after_html.select("#xhxm");

        //根据网页返回结果，登录判断
        //判断验证码是否正确，根据 查找 script  标签，验证码错误以及账户错误状态都在在第二个script 标签，密码错误在第一个标签
        Elements result = login_after_html.getElementsByTag("script");
        String check = result.get(1).toString();
//        System.out.println("result   :"+result);
//        System.out.println("check   :"+check);
//        System.out.println("result长度为："+result.size());
        //获取密码错误，密码错误在第一个script标签
        checkPassword = result.get(0).toString();//更新变量值
        //根据语句长度判断，登录状态
        int compCodeLength  = check.length();
        if (compCodeLength == 107){

            userInfo.setLoginStatus("验证码错误");

        }else if (compCodeLength == 122){

            userInfo.setLoginStatus("账户错误");

        }else if (checkPassword.length() == 78 ) {
            //判断密码错误次数
            //<script language="javascript">alert('密码错误，您还有3次尝试机会！如忘记密码，请与教务处联系!');</script>
            Pass = checkPassword.substring(checkPassword.indexOf("<script language=") + 45, checkPassword.indexOf(");</script>") - 21);
            userInfo.setLoginStatus("密码错误还有"+Pass+"次尝试机会");
        }else if (checkPassword.length() == 97){

            out = 97;//超出次数 截取数据长度
            //<script language="javascript">alert('密码错误，您密码输入错误已达5次，账号已锁定无法登录，次日自动解锁！如忘记密码，请与教务处联系!');</script>
            userInfo.setLoginStatus("密码输入错误已达5次");
        }else{

            userInfo.setLoginStatus("登录正常");

            //登录成功后
            String studentName = name_text.toString();
            //正则表达
            //<span id="xhxm">游显达同学</span>
            String name=studentName.substring(studentName.indexOf(">")+1, studentName.indexOf("同"));
            //System.out.println(name);

            LoginInfo loginInfo = new LoginInfo();
            loginInfo.setStudentName(name);//名字传回前端

            userInfo.setStudentName(name);
            userInfo.setUserId(username);

        }

        return userInfo;
    }

    /**
     * 获取考试安排方法
     * @Date 2021-8-7
     * @Author 游同学
     * @return*/
    public ExamInfo getExamInfo(String StudentID, String StudentName, String xnd, String xqd, String session) throws UnsupportedEncodingException {
        ExamInfo_text.clear();//清空集合存储的数据
        ExamInfo.infos.clear();

        loginCookie.setValue(session);//将前端传入的session注入cookie中
        //需要分 三步 进行，第一步访问查询考试安排界面，拿到源码的__VIEWSTATE值
        //第一步
        // 将普通字符创转换成application/x-www-from-urlencoded字符串
        String urlStudentName = URLEncoder.encode(StudentName, "gb2312");
        System.out.println(urlStudentName);
        ForestResponse FirstHTml = jw.getExamFirst(StudentID,urlStudentName,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //用jsoup解析
        String html = FirstHTml.getContent();
        Document First = Jsoup.parse(html);
        Elements elements = First.select("#form1 > input[type=hidden]:nth-child(3)");

        String VIEWSTATE = elements.toString();
        String __VIEWSTATE = VIEWSTATE.substring(VIEWSTATE.indexOf("value=")+7, VIEWSTATE.indexOf(">")-1);
        String URLEncodeVIEWSTATE = URLEncoder.encode(__VIEWSTATE, "gb2312");
        System.out.println("VIEWSTATE值为》》》》》》"+__VIEWSTATE);

        //第二步， 很扯，查询成绩界面默认打开显示的是当前学年成绩，而这个页面中的__VIEWSTATE去请求查询，默认显示的学年学期，没有数据，
        //而每一次查询不同学年和学期，__VIEWSTATE的值来自上一个查询的响应，，也就是说默认的__VIEWSTATE值是来自上级某个页面，获取不到
        //这里采用，拿页面默认学年的__VIEWSTATE值先去请求当前学年的第1学期，不管有没有考试安排，页面都会生成__VIEWSTATE值，就可以拿这个值去请求查询其他学期的考试安排

        //说简单点就是，当前查询学期考试安排中的__VIEWSTATE不能查当前学期
        ExamBody examBody = new ExamBody();
        examBody.setXnd("2015-2016");
        examBody.setXqd("1");
        examBody.set__EVENTTARGET("xqd");
        examBody.set__VIEWSTATE(URLEncodeVIEWSTATE);

        ForestResponse exam_html_second = jw.getExamInfo(StudentID,urlStudentName,examBody,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //获取结果，交给Jsoup
        String result_html_second = exam_html_second.getContent();
        Document result_second = Jsoup.parse(result_html_second);
        Elements elements_VIEWSTATUS = result_second.select("#form1 > input[type=hidden]:nth-child(3)");
        String VIEW = elements_VIEWSTATUS.toString();
        String ViewString = VIEW.substring(VIEW.indexOf("value=")+7, VIEW.indexOf(">")-1);
        String NewView = URLEncoder.encode(ViewString, "gb2312");


        //第三步
        //将前端传入的参数注入Body实体类，用于请求
        ExamBody PostExamBody = new ExamBody();
        PostExamBody.setXnd(xnd);
        PostExamBody.setXqd(xqd);
        PostExamBody.set__EVENTTARGET("xqd");
        PostExamBody.set__VIEWSTATE(NewView);

        ForestResponse exam_html = jw.getExamInfo(StudentID,urlStudentName,PostExamBody,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //获取结果，交给Jsoup
        String result_html = exam_html.getContent();
        Document result = Jsoup.parse(result_html);
//        System.out.println(result);
        //这里需要把每个考试，解析出来存在实体类里面
        //测试
        Elements element=result.getElementsByTag("td");
//        System.out.println(element);
        for(int i=8;i<element.size();i++){//8后为有效数据
            Element textContent = element.get(i);
            String Content = textContent.text();
            ExamInfo_text.add(Content);//把文本内容存入集合
        }
        for(int i=0;i<ExamInfo_text.size()/8;i++){
            //每循环一次，就是创建一个考试安排对象
            ExamInfo.Info info = new ExamInfo.Info();
            info.setID(ExamInfo_text.get(i*8));
            info.setName(ExamInfo_text.get(i*8+1));
            info.setStudentName(ExamInfo_text.get(i*8+2));
            info.setTime(ExamInfo_text.get(i*8+3));
            info.setPlace(ExamInfo_text.get(i*8+4));
            info.setHow(ExamInfo_text.get(i*8+5));
            info.setNumber(ExamInfo_text.get(i*8+6));
            info.setWhere(ExamInfo_text.get(i*8+7));

            ExamInfo.infos.add(info);//存入实体类

        }
        return examInfo;


    }

    /**
     * 查询成绩方法
     * */
    public ScoresInfo getScrodInfo(String username, String studentName, String xn , String xq, String session) throws UnsupportedEncodingException {
        //清空上一次查询到的成绩信息
        SoresInfo_text.clear();
        ScoresInfo.infos.clear();

        //注入session
        loginCookie.setValue(session);

        //%25D3%25CE%25CF%25D4%25B4%25EF
//        String urlStudentName = URLEncoder.encode(studentName, "gb2312");
        //如果前端使用JSON传参，不需要在转码

        Map<String, String> user = new HashMap<String, String>();
        user.put("xh",username);
        user.put("xm",studentName);
        user.put("gnmkdm","N121613");
        System.out.println(studentName);
//        System.out.println(urlStudentName);

        //模拟请求考试查询界面
        ForestResponse scrodFirst_html = jw.getScrodFirst(user,username,(request, cookies) -> {
            cookies.addCookie(loginCookie);

            System.out.println("查询成绩第一步界面cookie"+loginCookie.getValue());
        });

        //获取结果，交给Jsoup拿到  <input type="hidden" name="__VIEWSTATE" value="dDwtMTg1OTAwOTY4.....      的value
        String result_html = scrodFirst_html.getContent();
        Document result = Jsoup.parse(result_html);
        System.out.println(result);
        Elements  __VIEWSTATE_text = result.select("html body form#Form1 input");
        System.out.println("获取到的值为："+__VIEWSTATE_text.toString());
        String __VIEWSTATE = __VIEWSTATE_text.get(2).toString();
        String value=__VIEWSTATE.substring(__VIEWSTATE.indexOf("value=")+7, __VIEWSTATE.indexOf(">")-1);

        String URLEncode__VIEWSTATE = URLEncoder.encode(value, "gb2312");

        System.out.println(value);

        //解析出可以选择的学年
//        System.out.println(result_html);
        Elements xns = result.getElementsByTag("option");
        System.out.println(xns);



        getInfo(xn , xq, username, studentName, URLEncode__VIEWSTATE, loginCookie.getValue());

        return scoresInfo;
    }

    public ScoresInfo getInfo(String xn , String xq, String username, String StudentName, String __VIEWSTATE, String session) throws UnsupportedEncodingException {
        loginCookie.setValue(session);
        // 将普通字符创转换成application/x-www-from-urlencoded字符串
        String urlStudentName = URLEncoder.encode(StudentName, "gb2312");

        Map<String, String> query = new HashMap<String, String>();
        query.put("xh",username);
        query.put("xm",urlStudentName);
        query.put("gnmkdm","N121613");

        ScrodBody scrodBody = new ScrodBody();
        scrodBody.set__EVENTTARGET("");
        scrodBody.set__EVENTARGUMENT("");
        scrodBody.set__VIEWSTATE(__VIEWSTATE);
        scrodBody.setHidLanguage("");
        scrodBody.setDdlXN(xn);
        scrodBody.setDdlXQ(xq);
        scrodBody.setDdl_kcxz("");
        scrodBody.setBtn_xq("%D1%A7%C6%DA%B3%C9%BC%A8");


        //模拟请求考试查询界面
        System.out.println(loginCookie.getValue());
        ForestResponse scrod_html = jw.getScrodInfo(query,scrodBody,username,urlStudentName,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        System.out.println("查询成绩界面cookie"+loginCookie.getValue());


        String result_html = scrod_html.getContent();
        Document result = Jsoup.parse(result_html);
       // System.out.println(result);//打印出成绩界面源码
        //测试
        Elements element=result.getElementsByTag("td");
        for(int i=24;i<element.size()-14;i++){//23后为有效数据
                Element textContent = element.get(i);
                String Content = textContent.text();
            SoresInfo_text.add(Content);
        }
        for(int i=0;i<SoresInfo_text.size()/16;i++){
                ScoresInfo.Info cun = new ScoresInfo.Info();

                cun.setXn(SoresInfo_text.get(i*16));
                cun.setXq(SoresInfo_text.get(i*16+1));
                cun.setCode(SoresInfo_text.get(i*16+2));
                cun.setName(SoresInfo_text.get(i*16+3));
                cun.setType(SoresInfo_text.get(i*16+4));
                cun.setSpecialities(SoresInfo_text.get(i*16+5));
                cun.setCredit(SoresInfo_text.get(i*16+6));
                cun.setGPA(SoresInfo_text.get(i*16+7));
                cun.setScore(SoresInfo_text.get(i*16+8));
                cun.setMinor(SoresInfo_text.get(i*16+9));
                cun.setBk_cord(SoresInfo_text.get(i*16+10));
                cun.setBkjm_cord(SoresInfo_text.get(i*16+11));
                cun.setCx_cord(SoresInfo_text.get(i*16+12));
                cun.setXy(SoresInfo_text.get(i*16+13));
                cun.setBz(SoresInfo_text.get(i*16+14));
                cun.setCxbj(SoresInfo_text.get(i*16+15));

                ScoresInfo.infos.add(cun);
        }

        return scoresInfo;
    }

    /**
     * @Date 2021-8-8
     * @Author 游同学
     * @Descrip 查询补考考试安排方法
     * @return*/
    //第一步，请求补考考试安排默认界面获取__VIEWSTATUS
    public ExamInfo getExamAgainInfo(String username, String studentName, String xn , String xq, String session) throws UnsupportedEncodingException {
        ExamAgainInfo_text.clear();//清空集合存储的数据
        ExamInfo.infos.clear();

        loginCookie.setValue(session);//将前端传入的session注入cookie中

        //需要分 三步 进行，第一步访问查询考试安排界面，拿到源码的__VIEWSTATE值
        //第一步
        // 将普通字符创转换成application/x-www-from-urlencoded字符串
        String urlStudentName = URLEncoder.encode(studentName, "gb2312");
        System.out.println(urlStudentName);
        ForestResponse FirstHTml = jw.getExamAgainInfoFirst(username,urlStudentName,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //用jsoup解析
        String html = FirstHTml.getContent();
        Document First = Jsoup.parse(html);
        Elements elements = First.select("#Form1 > input[type=hidden]:nth-child(3)");

        String VIEWSTATE = elements.toString();
        String __VIEWSTATE = VIEWSTATE.substring(VIEWSTATE.indexOf("value=")+7, VIEWSTATE.indexOf(">")-1);
        String URLEncodeVIEWSTATE = URLEncoder.encode(__VIEWSTATE, "gb2312");
        System.out.println("补考考试安排VIEWSTATE值为》》》》》》"+__VIEWSTATE);

        //第二步
        //说简单点就是，当前查询学期考试安排中的__VIEWSTATE不能查当前学期
        ExamBody examBody = new ExamBody();
        examBody.setXnd("2015-2016");
        examBody.setXqd("1");
        examBody.set__EVENTTARGET("xqd");
        examBody.set__VIEWSTATE(URLEncodeVIEWSTATE);

        ForestResponse exam_html_second = jw.getExamAgainInfo(username,urlStudentName,examBody,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //获取结果，交给Jsoup
        String result_html_second = exam_html_second.getContent();
        Document result_second = Jsoup.parse(result_html_second);
        Elements elements_VIEWSTATUS = result_second.select("#Form1 > input[type=hidden]:nth-child(3)");
        String VIEW = elements_VIEWSTATUS.toString();
        String ViewString = VIEW.substring(VIEW.indexOf("value=")+7, VIEW.indexOf(">")-1);
        String NewView = URLEncoder.encode(ViewString, "gb2312");

        //第三步
        //将前端传入的参数注入Body实体类，用于请求
        ExamBody PostExamBody = new ExamBody();
        PostExamBody.setXnd(xn);
        PostExamBody.setXqd(xq);
        PostExamBody.set__EVENTTARGET("xqd");
        PostExamBody.set__VIEWSTATE(NewView);

        ForestResponse exam_html = jw.getExamAgainInfo(username,urlStudentName,PostExamBody,(request, cookies) -> {
            cookies.addCookie(loginCookie);
        });

        //获取结果，交给Jsoup
        String result_html = exam_html.getContent();
        Document result = Jsoup.parse(result_html);
//        System.out.println(result);
        //这里需要把每个考试，解析出来存在实体类里面
        //测试
        Elements element = result.getElementsByTag("td");
        System.out.println(element);
        for(int i=7;i<element.size();i++){//7后为有效数据
            Element textContent = element.get(i);
            String Content = textContent.text();
            ExamAgainInfo_text.add(Content);//把文本内容存入集合
        }
        for(int i=0;i<ExamAgainInfo_text.size()/7;i++){
            //每循环一次，就是创建一个考试安排对象
            ExamInfo.Info info = new ExamInfo.Info();
            info.setID(ExamAgainInfo_text.get(i*7));
            info.setName(ExamAgainInfo_text.get(i*7+1));
            info.setStudentName(ExamAgainInfo_text.get(i*7+2));
            info.setTime(ExamAgainInfo_text.get(i*7+3));
            info.setPlace(ExamAgainInfo_text.get(i*7+4));
            info.setNumber(ExamAgainInfo_text.get(i*7+5));
            info.setHow(ExamAgainInfo_text.get(i*7+6));

            ExamInfo.infos.add(info);//存入实体类

        }
        return examInfo;


    }


}
 